
/*#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "GLu32.lib")
#pragma comment(lib, "PhysXLoader.lib" )
#pragma comment(lib, "NxCooking.lib")
#pragma comment(lib, "NxCharacter.lib")
#pragma comment(lib, "glew32.lib" )*/

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <NxPhysics.h>

#include <SDL.h>
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include "common/nvTime.hh"

#include "Scene.hh"

static int Frames = 0;
static double Msecs = .0;

// Physics
static  NxPhysicsSDK*   gPhysicsSDK = NULL;
static  NxScene*	      gScene      = NULL;
static  SceneObject*    gCube			= NULL;
static  Cube*           gScenePlane = NULL;
static  nv::quaternionf gQuat;

#undef main

#define screen_width 1280
#define screen_height 1024
#define true 1
#define false 0 //You might have to declare True and False.


float randFloat( float aMax )
{
   float lTmp = ( rand() / ( float ) RAND_MAX ) * 2.0f * aMax - aMax;
   return lTmp;
}

SceneObject* createCube( const nv::vec3f& size, const nv::vec3f& pos )
{
   SceneObject* lTmp = Scene.addSceneObject( new Cube( size ) );
   nv::quaternionf lTmpQuat( nv::vec3f::VUNITX, 0.0f );
   lTmpQuat.get_value( lTmp->mOrientation );
   lTmp->mOrientation.set_translate( pos );
   
   // Create body
   NxBodyDesc bodyDesc;
   bodyDesc.angularDamping = 0.0001f;
  
   bodyDesc.linearVelocity = NxVec3( randFloat( 0.0f ), randFloat( 20.0f ), randFloat( 0.0f ));
   bodyDesc.angularVelocity = NxVec3( randFloat( 20.0f ), 
                                      randFloat( 20.0f ), 
                                      randFloat( 0.0f ) );
   NxBoxShapeDesc boxDesc;
   boxDesc.dimensions = NxVec3( size[ 0 ], size[ 1 ], size[ 2 ] );

   NxActorDesc actorDesc;
   actorDesc.shapes.pushBack( &boxDesc );
   actorDesc.body			= &bodyDesc;
   actorDesc.density		= 100.0f;
   actorDesc.globalPose.setColumnMajor44( lTmp->mOrientation.get_value() );                            
   NxActor *actor = gScene->createActor( actorDesc );
   actor->setMaxAngularVelocity( 100.0f );
   actor->userData = ( void* ) lTmp;
   
   return lTmp;
}


int main(int argc, char *argv[])
{
 srand( time( NULL ) );
 srand( rand() );
// Load SDL
  if (SDL_Init(SDL_INIT_VIDEO) != 0) {
    fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
    return false;
  }
  atexit(SDL_Quit); // Clean it up nicely :)

  //To use OpenGL, you need to get some information first,
  const SDL_VideoInfo *info = SDL_GetVideoInfo();
  if(!info) {
    /* This should never happen, if it does PANIC! */
    fprintf(stderr, "Video query failed: %s\n", SDL_GetError());
    return false;
  }
  
	int bpp = 32;
	
	SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
	SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
	SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
	SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
	SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 16 );
	SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 );
	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, true );
	
  //screen is no longer used, as openGL does all the drawing now!
  if (SDL_SetVideoMode(screen_width, screen_height, bpp, SDL_FULLSCREEN | SDL_OPENGL ) == 0) {
    fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
    return false;
  }
   
   //psyx 
   NxPhysicsSDKDesc desc;
   std::cout << "PhysicsSDK" << std::endl;

   gPhysicsSDK = NxCreatePhysicsSDK( NX_PHYSICS_SDK_VERSION, NULL );
   if( gPhysicsSDK == NULL )
   {
      std::cout << "PhysicsSDK creation error" << std::endl;
      return EXIT_FAILURE;
   }
   gPhysicsSDK->setParameter( NX_SKIN_WIDTH, 0.01f );
   
   std::cout << "HW: " << ( bool )( gPhysicsSDK->getHWVersion() != NX_HW_VERSION_NONE ) << std::endl;
   bool hasHW = gPhysicsSDK->getHWVersion() != NX_HW_VERSION_NONE;
   
   
   //plane
   gScenePlane = new Cube( nv::vec3f( 20.0f, 1.0f, 20.0f ) );
   gScenePlane->mOrientation.set_translate( nv::vec3f( 0.0f, -5.0f, -30.0f ) );

   // Create a scene
   NxSceneDesc sceneDesc;
   sceneDesc.simType = hasHW ? NX_SIMULATION_HW : NX_SIMULATION_SW;
   sceneDesc.gravity = NxVec3( 0.0f, -9.81f, 0.0f );

   // Enable Active Transform Notification
   //sceneDesc.flags |= NX_SF_ENABLE_ACTIVETRANSFORMS;
   gScene = gPhysicsSDK->createScene( sceneDesc );
   gScene->setTiming( 1.0f / 60.f, 16, NX_TIMESTEP_VARIABLE);
   
   std::cout << "gScene" << std::endl;
   if( gScene == NULL )
   {
      std::cout << "gScene creation error" << std::endl;
      return EXIT_FAILURE;
   }
    
	static GLint buf[1], sbuf[1];
	glGetIntegerv (GL_SAMPLE_BUFFERS_ARB, buf);
	printf ("number of sample buffers is %d\n", buf[0]);
	glGetIntegerv (GL_SAMPLES_ARB, sbuf);
	printf ("number of samples is %d\n", sbuf[0]);
   
	// Set default material
	NxMaterial* defaultMaterial = gScene->getMaterialFromIndex( 0 );
	defaultMaterial->setRestitution( 0.5f );
	defaultMaterial->setStaticFriction( 0.5f );
	defaultMaterial->setDynamicFriction( 0.5f );    
	
   //plane actor
   NxBoxShapeDesc boxDesc;
	boxDesc.dimensions = NxVec3( 20.0f, 1.0f, 20.0f );

	NxActorDesc actorDesc;
	actorDesc.shapes.pushBack( &boxDesc );
	actorDesc.density	= 10.0f;
	actorDesc.globalPose.setColumnMajor44( gScenePlane->mOrientation.get_value() );                                                       
	NxActor *actor = gScene->createActor( actorDesc );
	actor->userData = ( void * )gScenePlane;  


   // Load an OpenGL texture.
   // We could directly use a sf::Image as an OpenGL texture (with its Bind() member function),
   // but here we want more control on it (generate mipmaps, ...) so we create a new one
   GLuint Texture = 0;
   {
      SDL_Surface * lImage = SDL_LoadBMP("datas/opengl/texture.bmp");
      
      std::cout << "Loading image" << std::endl;
      
      if ( !lImage )
      {
         std::cout << "Can't load datas/opengl/texture.bmp" << std::endl;
         return EXIT_FAILURE;
      }
            
      std::cout << "Creating texture" << std::endl;
      glGenTextures(1, &Texture);
      glBindTexture(GL_TEXTURE_2D, Texture);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
      GLfloat largest_supported_anisotropy;
      glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy );
      glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy );
      glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
		glTexImage2D( GL_TEXTURE_2D, 0, 3, lImage->w, lImage->h, 0, GL_BGR, GL_UNSIGNED_BYTE, lImage->pixels );
      
      SDL_FreeSurface( lImage );
   }
   
   GLuint Texture1 = 0;
   {
      SDL_Surface * lImage = SDL_LoadBMP("datas/opengl/background.bmp");
      
      std::cout << "Loading image" << std::endl;
      
      if ( !lImage )
      {
         std::cout << "Can't load datas/opengl/background.bmp" << std::endl;
         return EXIT_FAILURE;
      }
            
      std::cout << "Creating texture" << std::endl;
      glGenTextures(1, &Texture1);
      glBindTexture(GL_TEXTURE_2D, Texture1);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
      GLfloat largest_supported_anisotropy;
      glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy );
      glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy );
      glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
		glTexImage2D( GL_TEXTURE_2D, 0, 3, lImage->w, lImage->h, 0, GL_BGR, GL_UNSIGNED_BYTE, lImage->pixels );

      
      SDL_FreeSurface( lImage );
   }

    // Enable Z-buffer read and write
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);
    glClearDepth(1.f);
		glEnable(GL_MULTISAMPLE_ARB);

    // Setup a perspective projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.f, screen_width/( GLdouble )screen_height, 1.f, 500.f);

    // Bind our texture
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, Texture);
    glColor4f(1.f, 1.f, 1.f, 0.5f);
            
	float size = 0.5f;
	int w = 10;
	int h = 10;
	
	for( int x = 0; x < w; ++x )
	{
		for( int z = 0; z < h; ++z )
		{
			float posz = -h - 20.0f + z * 1.01f;
			float posx = -( w / 2 ) + x * 1.01f;
			createCube( nv::vec3f( size, size, size ), nv::vec3f( posx, 5, posz ) );
		}
	}
       
    nv::Timer Clock;
    Clock.start();
    
    // Create an SDL event object through which we may monitor any events
    SDL_Event event;
    
    //Loop forever - could be dangerous! But we take the proper precautions below
    while( true ){
        if ( SDL_PollEvent( &event ) )
        {
        
            //Check the event structure -> type member
            switch( event.type ){
            
                //Event where user has closed the window (x button). Break from the loop
                case SDL_QUIT:
                    return EXIT_SUCCESS;
                                        
                //Event where the user has pressed a key on the keyboard
                case SDL_KEYDOWN:                
                    //If the key was the escape key, break from the loop
                    if( event.key.keysym.sym == SDLK_ESCAPE ) 
                    {
                        return EXIT_SUCCESS;
                    }
                    if( event.key.keysym.sym == SDLK_s ) 
                    {
								float size = 0.5f;
								int w = 10;
								int h = 10;
								
								for( int x = 0; x < w; ++x )
								{
									for( int z = 0; z < h; ++z )
									{
										float posz = -h - 20.0f + z * 1.01f; 
										float posx = -( w >> 1 ) + x * 1.01f;
										createCube( nv::vec3f( size, size, size ), nv::vec3f( posx, 5, posz ) );
									}
								}
                    }
            }
        }
         
         // Start simulation (non blocking)
         float lTime = Clock.getElapsedTime();
         gScene->simulate( lTime );
         gScene->flushStream();         
         gScene->fetchResults( NX_RIGID_BODY_FINISHED, true );

         // Keep physics & graphics in sync
	      int nbActors = gScene->getNbActors();
	      NxActor** actors = gScene->getActors();
         float lTmp[ 16 ];
	      while( nbActors-- )
	      {
            NxActor* actor = *actors++;
		      if( !actor->userData ) { continue; }
		      if( actor->isSleeping() ) { continue; }
            
            actor->getGlobalPose().getColumnMajor44( lTmp );
            SceneObject *sceneObject = ( SceneObject* ) actor->userData;
            sceneObject->mOrientation.set_value( lTmp );
           //glLoadMatrixf( lTmp );
         }
         // Clear depth buffer
         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
         glBindTexture( GL_TEXTURE_2D, Texture1 );
         gScenePlane->render();
         glBindTexture( GL_TEXTURE_2D, Texture );
         Scene.renderScene();
                  
         //Clock.Reset();
         ++Frames;
         Msecs += lTime;
         if( Msecs >= 1.0 )
         {
            printf( "Frames %d\n", Frames );
            Frames = 0;
            Msecs = 0.0;
         }
         
         // Finally, display the rendered frame on screen
         SDL_GL_SwapBuffers();                     
    }   
         
         
    // Don't forget to destroy our texture
    delete gScenePlane;
    glDeleteTextures( 1, &Texture );
    
    gPhysicsSDK->releaseScene( *gScene );
    NxReleasePhysicsSDK( gPhysicsSDK );

    
    return EXIT_SUCCESS;
}
